using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using XC.Framework.Security.RSAUtil;
using System.IO;

namespace SharpSoft
{
    /// <summary>
    /// 提供一些基础的加密算法
    /// </summary>
    public static class Security
    {
        public static byte[] GetMD5HashBytes(string input)
        {
            MD5 md5 = MD5.Create();
            byte[] bs = Encoding.UTF8.GetBytes(input);
            bs = md5.ComputeHash(bs);
            return bs;
        }
        /// <summary>
        /// 计算MD5
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string MD5Hash(string input)
        {
            MD5 md5 = MD5.Create();
            byte[] bs = Encoding.UTF8.GetBytes(input);
            bs = md5.ComputeHash(bs);
            StringBuilder s = new System.Text.StringBuilder();
            foreach (byte b in bs)
            {
                s.Append(b.ToString("x2").ToUpper());
            }
            return s.ToString();
        }
        /// <summary>
        /// 计算md5字节
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string MD5Hash(byte[] buf)
        {
            MD5 md5 = MD5.Create();
            var bs = md5.ComputeHash(buf);
            System.Text.StringBuilder s = new System.Text.StringBuilder();
            foreach (byte b in bs)
            {
                s.Append(b.ToString("x2").ToUpper());
            }
            return s.ToString();
        }
        /// <summary>
        /// 计算数据流的哈希值
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static string MD5Hash(Stream stream)
        {
            MD5 md5 = MD5.Create();
            var bs = md5.ComputeHash(stream);
            System.Text.StringBuilder s = new System.Text.StringBuilder();
            foreach (byte b in bs)
            {
                s.Append(b.ToString("x2").ToUpper());
            }
            return s.ToString();
        }
        /// <summary>
        /// 使用utf8和base64加密字符串
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string Base64Encrypt(string input)
        {
            byte[] bs = System.Text.Encoding.UTF8.GetBytes(input);
            return Convert.ToBase64String(bs);
        }
        /// <summary>
        /// 使用utf8和base64解密字符串
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string Base64Decrypt(string input)
        {
            byte[] bs = Convert.FromBase64String(input); ;
            return System.Text.Encoding.UTF8.GetString(bs);
        }
        /// <summary>
        /// Skip32算法，使用默认密钥加密生成32位整数的离散唯一数据。
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static uint Skip32Encrypt(uint source)
        {
            var skip32 = new Encryption.Skip32Cipher(new byte[] { 12, 17, 45, 87, 96, 223, 1, 0, 7, 91 });
            return skip32.Encrypt(source);
        }
        /// <summary>
        /// 使用默认密钥解密Skip32
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static uint Skip32Decrypt(uint value)
        {
            var skip32 = new Encryption.Skip32Cipher(new byte[] { 12, 17, 45, 87, 96, 223, 1, 0, 7, 91 });
            return skip32.Decrypt(value);
        }
        /// <summary>
        /// Skip32算法
        /// </summary>
        /// <param name="source"></param>
        /// <param name="key">键，10个字节</param>
        /// <returns></returns>
        public static uint Skip32Encrypt(uint source, byte[] key)
        {
            var skip32 = new Encryption.Skip32Cipher(key);
            return skip32.Encrypt(source);
        }
        public static uint Skip32Decrypt(uint value, byte[] key)
        {
            var skip32 = new Encryption.Skip32Cipher(key);
            return skip32.Decrypt(value);
        }

        #region DES

        public static string DesEncrypt(string input, string key)
        {

            byte[] inputArray = Encoding.UTF8.GetBytes(input);
            var tripleDES = TripleDES.Create();
            var byteKey = Encoding.UTF8.GetBytes(key);
            byte[] allKey = new byte[24];
            Buffer.BlockCopy(byteKey, 0, allKey, 0, 16);
            Buffer.BlockCopy(byteKey, 0, allKey, 16, 8);
            tripleDES.Key = allKey;
            tripleDES.Mode = CipherMode.ECB;
            tripleDES.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = tripleDES.CreateEncryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }

        public static string DesDecrypt(string input, string key)
        {
            byte[] inputArray = Convert.FromBase64String(input);
            var tripleDES = TripleDES.Create();
            var byteKey = Encoding.UTF8.GetBytes(key);
            byte[] allKey = new byte[24];
            Buffer.BlockCopy(byteKey, 0, allKey, 0, 16);
            Buffer.BlockCopy(byteKey, 0, allKey, 16, 8);
            tripleDES.Key = allKey;
            tripleDES.Mode = CipherMode.ECB;
            tripleDES.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = tripleDES.CreateDecryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
            return Encoding.UTF8.GetString(resultArray);
        }

        #endregion

        #region RSA

        /// <summary>
        /// 生成Rsa密钥
        /// </summary>
        /// <param name="keytype"></param>
        /// <param name="keysize"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public static RsaKey RsaKeyGenerate(RsaKeyType keytype, int keysize = 2048, bool format = false)
        {
            RsaKey key = new RsaKey(keytype, keysize, format);
            key.Generate();
            return key;
        }

        public static string RsaEncrypt(RsaKey key, string data, RSAEncryptionPadding rsaEnPadding, Encoding encoding = null)
        {
            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }
            string result;
            switch (key.KeyType)
            {
                case RsaKeyType.XML:
                    RsaXmlUtil xml = new RsaXmlUtil(encoding, key.PublicKey, key.PrivateKey, key.KeySize);
                    result = xml.Encrypt(data, rsaEnPadding);
                    break;
                case RsaKeyType.Pkcs1:
                    RsaPkcs1Util pkcs1 = new RsaPkcs1Util(encoding, key.PublicKey, key.PrivateKey, key.KeySize);
                    result = pkcs1.Encrypt(data, rsaEnPadding);
                    break;
                case RsaKeyType.Pkcs8:
                    RsaPkcs8Util pkcs8 = new RsaPkcs8Util(encoding, key.PublicKey, key.PrivateKey, key.KeySize);
                    result = pkcs8.Encrypt(data, rsaEnPadding);
                    break;
                default:
                    throw new Exception("unkonw rsakeyType.");
            }
            return result;
        }

        public static string RsaDecrypt(RsaKey key, string data, RSAEncryptionPadding rsaEnPadding, Encoding encoding = null)
        {
            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }
            string result;
            switch (key.KeyType)
            {
                case RsaKeyType.XML:
                    RsaXmlUtil xml = new RsaXmlUtil(encoding, key.PublicKey, key.PrivateKey, key.KeySize);
                    result = xml.Decrypt(data, rsaEnPadding);
                    break;
                case RsaKeyType.Pkcs1:
                    RsaPkcs1Util pkcs1 = new RsaPkcs1Util(encoding, key.PublicKey, key.PrivateKey, key.KeySize);
                    result = pkcs1.Decrypt(data, rsaEnPadding);
                    break;
                case RsaKeyType.Pkcs8:
                    RsaPkcs8Util pkcs8 = new RsaPkcs8Util(encoding, key.PublicKey, key.PrivateKey, key.KeySize);
                    result = pkcs8.Decrypt(data, rsaEnPadding);
                    break;
                default:
                    throw new Exception("unkonw rsakeyType.");
            }
            return result;
        }


        public static string RsaSignData(RsaKey key, string data, HashAlgorithmName hashAlgorithmName, RSASignaturePadding rsaSignPadding, Encoding encoding = null)
        {
            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }
            string result;
            switch (key.KeyType)
            {
                case RsaKeyType.XML:
                    RsaXmlUtil xml = new RsaXmlUtil(encoding, key.PublicKey, key.PrivateKey, key.KeySize);
                    result = xml.SignData(data, hashAlgorithmName, rsaSignPadding);
                    break;
                case RsaKeyType.Pkcs1:
                    RsaPkcs1Util pkcs1 = new RsaPkcs1Util(encoding, key.PublicKey, key.PrivateKey, key.KeySize);
                    result = pkcs1.SignData(data, hashAlgorithmName, rsaSignPadding);
                    break;
                case RsaKeyType.Pkcs8:
                    RsaPkcs8Util pkcs8 = new RsaPkcs8Util(encoding, key.PublicKey, key.PrivateKey, key.KeySize);
                    result = pkcs8.SignData(data, hashAlgorithmName, rsaSignPadding);
                    break;
                default:
                    throw new Exception("unkonw rsakeyType.");
            }
            return result;
        }
        public static bool RsaVerifyData(RsaKey key, string data, string sign, HashAlgorithmName hashAlgorithmName, RSASignaturePadding rsaSignPadding, Encoding encoding = null)
        {
            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }
            bool result;
            switch (key.KeyType)
            {
                case RsaKeyType.XML:
                    RsaXmlUtil xml = new RsaXmlUtil(encoding, key.PublicKey, key.PrivateKey, key.KeySize);
                    result = xml.VerifyData(data, sign, hashAlgorithmName, rsaSignPadding);
                    break;
                case RsaKeyType.Pkcs1:
                    RsaPkcs1Util pkcs1 = new RsaPkcs1Util(encoding, key.PublicKey, key.PrivateKey, key.KeySize);
                    result = pkcs1.VerifyData(data, sign, hashAlgorithmName, rsaSignPadding);
                    break;
                case RsaKeyType.Pkcs8:
                    RsaPkcs8Util pkcs8 = new RsaPkcs8Util(encoding, key.PublicKey, key.PrivateKey, key.KeySize);
                    result = pkcs8.VerifyData(data, sign, hashAlgorithmName, rsaSignPadding);
                    break;
                default:
                    throw new Exception("unkonw rsakeyType.");
            }
            return result;
        }

        #endregion
    }
    /// <summary>
    /// RSA密钥
    /// </summary>
    public class RsaKey
    {
        public RsaKey(RsaKeyType keytype, int keysize, bool format = true)
        {
            this.KeyType = keytype;
            this.KeySize = keysize;
            formated = format;

        }
        private bool formated = false;

        /// <summary>
        /// 密钥长度
        /// </summary>
        public int KeySize { get; private set; } = 2048;
        /// <summary>
        /// 密钥类型
        /// </summary>
        public RsaKeyType KeyType { get; private set; }
        /// <summary>
        /// 公钥
        /// </summary>
        public string PublicKey { get; set; }
        /// <summary>
        /// 私钥
        /// </summary> 
        public string PrivateKey { get; set; }

        internal void Generate()
        {
            List<string> keyList;
            switch (this.KeyType)
            {
                case RsaKeyType.XML:
                    keyList = RsaKeyGenerator.XmlKey(this.KeySize);
                    this.PrivateKey = keyList[0];
                    this.PublicKey = keyList[1];
                    break;
                case RsaKeyType.Pkcs1:
                    keyList = RsaKeyGenerator.Pkcs1Key(KeySize, formated);
                    this.PrivateKey = keyList[0];
                    this.PublicKey = keyList[1];
                    break;
                case RsaKeyType.Pkcs8:
                    keyList = RsaKeyGenerator.Pkcs8Key(KeySize, formated);
                    this.PrivateKey = keyList[0];
                    this.PublicKey = keyList[1];
                    break;
                default:
                    break;
            }
        }
        /// <summary>
        /// 转换密钥类型
        /// </summary>
        /// <param name="newtype"></param>
        /// <returns></returns>
        public RsaKey ConvertTo(RsaKeyType newtype)
        {
            if (KeyType == newtype)
            {
                return new RsaKey(KeyType, KeySize, this.formated) { PrivateKey = this.PrivateKey, PublicKey = this.PublicKey };
            }
            RsaKey newkey = new RsaKey(newtype, this.KeySize, this.formated);
            newkey.KeyType = newtype;
            if (KeyType == RsaKeyType.XML)
            {
                if (newtype == RsaKeyType.Pkcs1)
                {
                    newkey.PrivateKey = RsaKeyConvert.PrivateKeyXmlToPkcs1(this.PrivateKey);
                    newkey.PublicKey = RsaKeyConvert.PublicKeyXmlToPem(this.PublicKey);
                }
                else if (newtype == RsaKeyType.Pkcs8)
                {
                    newkey.PrivateKey = RsaKeyConvert.PrivateKeyXmlToPkcs8(this.PrivateKey);
                    newkey.PublicKey = RsaKeyConvert.PublicKeyXmlToPem(this.PublicKey);
                }
            }
            else if (KeyType == RsaKeyType.Pkcs1)
            {
                if (newtype == RsaKeyType.XML)
                {
                    newkey.PrivateKey = RsaKeyConvert.PrivateKeyPkcs1ToXml(this.PrivateKey);
                    newkey.PublicKey = RsaKeyConvert.PublicKeyPemToXml(this.PublicKey);
                }
                else if (newtype == RsaKeyType.Pkcs8)
                {
                    newkey.PrivateKey = RsaKeyConvert.PrivateKeyPkcs1ToPkcs8(this.PrivateKey);
                    newkey.PublicKey = this.PublicKey;
                }
            }
            else if (KeyType == RsaKeyType.Pkcs8)
            {
                if (newtype == RsaKeyType.XML)
                {
                    newkey.PrivateKey = RsaKeyConvert.PrivateKeyPkcs8ToXml(this.PrivateKey);
                    newkey.PublicKey = RsaKeyConvert.PublicKeyPemToXml(this.PublicKey);
                }
                else if (newtype == RsaKeyType.Pkcs1)
                {
                    newkey.PrivateKey = RsaKeyConvert.PrivateKeyPkcs8ToPkcs1(this.PrivateKey);
                    newkey.PublicKey = this.PublicKey;
                }
            }
            return newkey;
        }
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="data"></param>
        /// <param name="rsaEnPadding"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public string Encrypt(string data, RSAEncryptionPadding rsaEnPadding, Encoding encoding = null)
        {
            return Security.RsaEncrypt(this, data, rsaEnPadding, encoding);
        }
        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="data"></param>
        /// <param name="rsaEnPadding"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public string Decrypt(string data, RSAEncryptionPadding rsaEnPadding, Encoding encoding = null)
        {
            return Security.RsaDecrypt(this, data, rsaEnPadding, encoding);
        }


    }
    /// <summary>
    /// rsa密钥类型
    /// </summary>
    public enum RsaKeyType
    {
        XML,
        Pkcs1,
        Pkcs8,

    }
}
